
#include <cstdlib>
#include <string>
#include <iostream>
#include <vector>
#include <windows.h>
#include <conio.h>
#include <sstream>

#include "conversation.h"
#include "man.h"
#include "console_ui.h"
#include "words.h"
#include "loader.h"

using namespace std;

void _SetCursorVisable(bool v) {
    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_CURSOR_INFO cInfo;
    GetConsoleCursorInfo(handle, &cInfo);
    cInfo.bVisible = v;
    SetConsoleCursorInfo(handle, &cInfo);
}

unsigned short _ReadKey() {
    fflush(stdin);
    unsigned char chHigh = 0, chLow = 0;

    chLow = getch();

    if (chLow == 224) {
        chHigh = getch();
    }

    return (chHigh << 8) | chLow;
}

void _GotoXY(short x, short y) {
    COORD coord = {x, y};
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}

void _ClearScreen() {
    system("cls");
}

void _FillBlank(int nline) {
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    int col;

    GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
    col = csbi.srWindow.Right - csbi.srWindow.Left + 1;

    char *blank = (char*)malloc(col);
    memset(blank, ' ', col - 1);
    blank[col - 1] = 0;
    for (int i = 0; i < nline; ++i)
        printf(blank);
    free(blank);
}

void ConsoleUI::DrawMan(int state) {
    _SetCursorVisable(0);

    if (state >= 6 && state < 0) {
        cout << "Error: state out of range 0 ~ 5" << endl;
        return;
    }
    const char** pLine = MAN_PIC[state];
    short baseX = 27, baseY = 5;

    for (int i = 0; i < 9; ++i) {
        _GotoXY(baseX, baseY + i);
        cout << pLine[i];
    }
}

void ConsoleUI::ClearAskPanel () {
    _SetCursorVisable(0);

    short baseX = 23, baseY = 18;
    _FillBlank(1);

    _GotoXY(baseX, baseY);
    cout << "Give your letter: [ ]" << endl;
}

char ConsoleUI::AskLetter() {
    short baseX = 23, baseY = 18, key;
    char ans = 0;
    _FillBlank(1);

    _GotoXY(baseX, baseY);
    cout << "Give your letter: [ ]" << endl;
    _GotoXY(baseX + 19, baseY);

    _SetCursorVisable(1);

    for (;;) {
        key = _ReadKey();
        if ((key >= 'a' && key <= 'z') ||
            (key >= 'A' && key <= 'Z')) {
            ans = (char) key;
            putch(key);
            _GotoXY(baseX + 19, baseY);
        }
        if (key == 13 && ans != 0)
            break;
    }

    _SetCursorVisable(0);

    _GotoXY(baseX, baseY + 1);
    // printf("You gave letter \"%c\"...\n", ans);

    return ans;
}

bool ConsoleUI::_Conversation(vector<Segment> &convs) {
    _GotoXY(10, 3);
    _FillBlank(1);
    _GotoXY(10, 3);

    for (Segment cv: convs) {
        if (cv.interval == -1)
            return 0;

        for (char ch: cv.text) {
            cout << ch;
            Sleep(20);
        }
        Sleep(cv.interval);
    }

    return 1;
}

bool* ConsoleUI::_AskYesNo(vector<Segment> &convs) {
    _Conversation(convs);
    char ch;
    cin >> ch;
    fflush(stdin);

    if (ch == 'N' || ch == 'n') {
        return new bool(0);
    }

    return new bool(1);
}

void ConsoleUI::FadeOff(float second) {
    _SetCursorVisable(0);

    int interval = second * 1000 / 20;
    for (int y = 0; y <= 20; ++y) {
        _GotoXY(0, y);
        _FillBlank(1);
        Sleep(interval);
    }
}

void* ConsoleUI::Dialog(vector<Segment> &convs, int type) {
    _SetCursorVisable(0);

    bool stop;
    void *ret = 0;

    _SetCursorVisable(1);

    switch (type) {

    case UI_DIALOG_CONVERSATION:
        stop = this->_Conversation(convs);

        if (stop) {
            for (char ch: string("  <>")) {
                cout << ch;
                Sleep(20);
            }
            _ReadKey();
        }

        break;

    case UI_DIALOG_ASK_YES_NO:
        ret = (void*)this->_AskYesNo(convs);
        break;

    }

    _SetCursorVisable(0);

    return ret;
}

void ConsoleUI::SetPanelWidth(int length) {
    this->wordLength = length;
}

void ConsoleUI::SetWordBuffer(char *buf) {
    this->buf = buf;
}

void ConsoleUI::RenderWordPanel() {
    _SetCursorVisable(0);

    _GotoXY(30, 16);
    _FillBlank(1);
    _GotoXY(30, 16);

    if (wordLength == 0 || buf == 0)
        return;

    for (int i = 0; i < wordLength; ++i) {
        if (buf[i] == 0)
            cout << "_";
        else
            cout << buf[i];
        cout << " ";
    }
}

void ConsoleUI::ReloadWords(vector<Word> &words) {
    cin.sync();
    _SetCursorVisable(1);

    int nShow = 5;
    short baseX = 10, baseY = 10, key, beginIndex = 0;
    unsigned char keyLow;

    for (;;) {
        _ClearScreen();
        _GotoXY(10, 3);
        cout << "Enter word path: ";

        string path;

        cin >> path;

        switch (WordFileLoader::LoadFrom(path.c_str(), words)) {
        case LD_ERR_NO_FILE:
                _GotoXY(10, 6);
                cout << "File not exists, press ENTER to retry.";
                _GotoXY(10, 7);
                cout << "or press EXIT to exit.";
                cin.sync();
                if (_ReadKey() == 27)
                    goto ret;
                break;
        case 0:
            goto view;
        }
    }
view:
    _SetCursorVisable(0);
    _GotoXY(10, 7);
    cout << "Loaded " << words.size() << " word(s) successfully.";
    _GotoXY(10, 8);
    cout << "Press ENTER to return.";

    _GotoXY(9, 9);
    cout << "+--";
    for (int i = 0; i < nShow; ++i) {
        _GotoXY(9, 10 + i);
        cout << "|";
    }
    _GotoXY(9, 9 + nShow);
    cout << "+--";

    for (;;) {
        for (int i = 0; i < nShow; ++i) {
            _GotoXY(baseX, baseY + i);
            _FillBlank(1);
        }

        for (int i = beginIndex, j = 0; i < words.size() && i < beginIndex + nShow; ++i, ++j) {
            _GotoXY(baseX, baseY + j);
            // printf("%s\t%s", words[i].text, words[i].describe);
            cout << "[" << i << "]";
            _GotoXY(baseX + 6, baseY + j);
            cout << words[i].text;
            _GotoXY(baseX + 26, baseY + j);
            cout << words[i].describe;
        }

        key = _ReadKey();

        if (key == 13) {
            goto ret;
        }


        keyLow = key & 0xff;

        if (keyLow == 224) {
            switch (key >> 8) {

            case 72:  // key: up arrow
                if (beginIndex > 0)
                    beginIndex--;
                break;

            case 80:  // key: down arrow
                if (beginIndex + nShow < words.size())
                    beginIndex++;
                break;

            }
        }
    }
ret:
    _SetCursorVisable(0);
    FadeOff(0.5);
    _ClearScreen();
    cin.sync();
}

void ConsoleUI::TwinklePanel() {
    _SetCursorVisable(0);

    _GotoXY(30, 16);

    stringstream ss;

    if (wordLength == 0 || buf == 0)
        return;

    for (int i = 0; i < wordLength; ++i) {
        if (buf[i] == 0)
            ss << "_";
        else
            ss << buf[i];
        ss << " ";
    }

    _GotoXY(30, 16);
    cout << ss.str();
    Sleep(250);

    _GotoXY(30, 16);
    _FillBlank(1);
    Sleep(250);

    _GotoXY(30, 16);
    cout << ss.str();
    Sleep(250);

    _GotoXY(30, 16);
    _FillBlank(1);
    Sleep(250);

    _GotoXY(30, 16);
    cout << ss.str();
    Sleep(250);
}

ConsoleUI::ConsoleUI() {
    _ClearScreen();
    _SetCursorVisable(0);
}

ConsoleUI::~ConsoleUI() {
    _SetCursorVisable(1);
}

int ConsoleUI::MainMenu() {
    int itemLength = 3;

    _GotoXY(10, 3);
    cout << "H A N G ~ M A N";
    _GotoXY(11, 4);
    cout << "/Spec. Game/";

    _GotoXY(12, 6);
    cout << "1. START GAME";

    _GotoXY(12, 8);
    cout << "2. LOAD WORDS";

    _GotoXY(12, 10);
    cout << "3. QUIT";

    unsigned short curX = 10, curY = 6, key;
    int keyLow, i = 0;

    for (;;) {
        _GotoXY(curX, curY + 2 * i);
        cout << ">";

        key = _ReadKey();

        _GotoXY(curX, curY + 2 * i);
        cout << " ";

        keyLow = key & 0xff;

        if (keyLow == 224) {
            switch (key >> 8) {

            case 72:  // key: up arrow
                i--;
                if (i < 0)
                    i = itemLength - 1;
                break;

            case 80:  // key: down arrow
                i++;
                if (i >= itemLength)
                    i = 0;
                break;

            }
        }

        if (keyLow == 13) {
            FadeOff(0.5);
            return i;
        }
    }
}

void ConsoleUI::OnStart() {
    _SetCursorVisable(0);
    _GotoXY(10, 5);
    cout << "+---------------------+";
    _GotoXY(10, 6);
    cout << "|      HANG MAN       |";
    _GotoXY(10, 7);
    cout << "|      Spec. Inc      |";
    _GotoXY(10, 8);
    cout << "+---------------------+";

    Sleep(1000);
    FadeOff(1);
}

void ConsoleUI::OnExit() {}
